package me.zingon.yourbatis.invoker;

import me.zingon.yourbatis.annotation.Param;
import me.zingon.yourbatis.annotation.Sql;
import me.zingon.yourbatis.db.DBConnection;
import me.zingon.yourbatis.factory.DBPoolFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MethodProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String sql = method.getAnnotation(Sql.class).value();
        List<Object> params=getParams(sql,method,args);
        return execSql(sql,params);
    }

    /**
     * 执行sql
     * @param sql 要执行的sql，参数已替换为？
     * @param params sql中按顺序出现的参数
     * @return 返回结果
     * @throws Exception 异常
     */
    private Object execSql(String sql, List params) throws Exception {
        DBConnection dbConnection = DBPoolFactory.getConnection();
        ResultSet rs=null;
        PreparedStatement ps=null;
        List result=null;
        try {
            Connection connection=dbConnection.getConnection();
            System.out.println(connection.toString()+" 执行");
            ps= connection.prepareStatement(sql);
            if(params!=null) {
                for (int i = 0; i < params.size(); i++) {
                    ps.setObject(i + 1, params.get(i));
                }
            }
            rs= ps.executeQuery();
            result=resultSetToList(rs);
        } finally {
            ps.close();
            dbConnection.release();
        }
        return result;
    }

    /**
     * 获取sql参数，按出现顺序排列，处理sql参数为？
     * @param sql
     * @param method
     * @param args
     * @return
     */
    List<Object> getParams(String sql,Method method,Object[] args){
        if(args ==null || args.length==0){
            return null;
        }
        Map<String, Object> argMap = getArgs(method, args);
        //sql中按顺序出现的参数
        List<Object> params = new ArrayList<>(args.length);

        Pattern r = Pattern.compile("#\\{(.*?)\\}");
        Matcher matcher = r.matcher(sql);

        String temp = null;
        while (matcher.find()) {
            temp = matcher.group(0);
            params.add(argMap.get(temp.substring(2, temp.length() - 1)));
            sql = sql.replace(temp, "?");
        }
        System.out.println("处理后sql：" + sql);
        return params;
    }


    /**
     * 获取方法参数名和实际参数的对应map
     * @param method 方法
     * @param args 参数
     * @return 对应map
     */
    private Map<String, Object> getArgs(Method method, Object[] args) {
        if(args ==null || args.length==0){
            return null;
        }
        Map<String, Object> result = new HashMap<>(args.length, 1);
        int size = args.length;
        Parameter[] parameters = method.getParameters();
        String key = null;
        for (int i = 0; i < size; i++) {
            if (parameters[i].isAnnotationPresent(Param.class)) {
                //Param注解有无value，有：key=注解value，无：key=参数名称
                key = parameters[i].getAnnotation(Param.class).value().equals("") ? parameters[i].getName() : parameters[i].getAnnotation(Param.class).value();
            } else {
                key = parameters[i].getName();
            }
            result.put(key, args[i]);
        }
        return result;
    }


    public static List<Map<String,Object>> resultSetToList(ResultSet rs) throws SQLException {
        List<Map<String,Object>> results=new ArrayList<Map<String,Object>>();
        ResultSetMetaData rsmd = rs.getMetaData();
        int colCount=rsmd.getColumnCount();
        List<String> colNameList=new ArrayList<String>();
        for(int i=0;i<colCount;i++){
            colNameList.add(rsmd.getColumnName(i+1));
        }
        while(rs.next()){
            for(int i=0;i<colCount;i++){
                Map map=new HashMap<String, Object>();
                String key=colNameList.get(i);
                Object value=rs.getString(colNameList.get(i));
                map.put(key, value);
                results.add(map);
            }
        }
        return results;
    }
}
